package logger

import (
	"io"
	"os"
	"time"

	"gitee.com/xinshortlink/internal/constants"
	"gitee.com/xinshortlink/internal/pkg/conf"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

func NewLog(conf *conf.Conf) (*zap.Logger, error) {
	// opt := &option{
	// 	level:      zapcore.InfoLevel,
	// 	timeLayout: time.RFC3339,
	// 	fields:     map[string]string{},
	// 	console:    true,
	// 	rotatelogs: false,
	// 	maxSize:    1024,
	// 	maxAge:     30,
	// 	maxBackups: 5,
	// 	compress:   false,
	// }

	// for _, f := range opts {
	// 	f(opt)
	// }

	opt := conf.Logger

	encoderConfig := zapcore.EncoderConfig{
		TimeKey:       "time",
		LevelKey:      "level",
		NameKey:       "logger",
		CallerKey:     "caller",
		MessageKey:    "mesasge",
		StacktraceKey: "stacktrace",
		LineEnding:    zapcore.DefaultLineEnding,
		EncodeLevel:   zapcore.LowercaseLevelEncoder,
		EncodeTime: func(t time.Time, pae zapcore.PrimitiveArrayEncoder) {
			pae.AppendString(t.Format(opt.TimeLayout))
		},
		EncodeDuration: zapcore.MillisDurationEncoder,
		EncodeCaller:   zapcore.ShortCallerEncoder,
	}

	var encoder zapcore.Encoder
	if conf.Server.Mode == constants.DebugMode {
		encoder = zapcore.NewConsoleEncoder(encoderConfig)
	} else {
		encoder = zapcore.NewJSONEncoder(encoderConfig)
	}

	files := make([]io.Writer, 0, len(opt.Files))

	if opt.Rotatelogs {
		for _, file := range opt.Files {
			l := &lumberjack.Logger{
				Filename:   file,
				MaxSize:    opt.MaxSize,
				MaxAge:     opt.MaxAge,
				MaxBackups: opt.MaxBackups,
				Compress:   opt.Compress,
			}
			files = append(files, l)
		}
	} else {
		for _, fileName := range opt.Files {
			file, err := os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY, 0775)
			if err != nil {
				return nil, err
			}
			files = append(files, file)
		}
	}

	if opt.Console {
		files = append(files, os.Stdout)
	}

	multierr := io.MultiWriter(files...)

	var level zapcore.Level
	switch opt.Level {
	case "debug":
		level = zapcore.DebugLevel
	case "info":
		level = zapcore.InfoLevel
	case "warn":
		level = zapcore.WarnLevel
	case "error":
		level = zapcore.ErrorLevel
	default:
		level = zapcore.InfoLevel
	}

	core := zapcore.NewCore(encoder,
		zapcore.AddSync(multierr),
		zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
			return lvl >= level
		}),
	)

	zap_options := []zap.Option{zap.AddCaller(), zap.ErrorOutput(os.Stderr)}

	for k, v := range opt.Fields {
		zap_options = append(zap_options, zap.Fields(zapcore.Field{Key: k, Type: zapcore.StringType, String: v}))
	}

	return zap.New(core, zap_options...), nil
}
